﻿using System.Collections.Generic;
using System.Linq;
using Metro.Kashikoi.Service.Common.Entity;
using Metro.Kashikoi.Service.Storage.Entity;
using Metro.Kashikoi.ServiceContract.Storage;

namespace Metro.Kashikoi.Service.Storage.Business
{
    /// <summary>
    /// This class implements the interface <c>IFacade</c>.
    /// </summary>
    class Facade : IFacade
    {
        #region IFacade Members

        /// <summary>
        /// Creates the inventory.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="inventory">The inventory.</param>
        /// <param name="stockAreaId">The stock area id.</param>
        public void CreateInventory(IUnitOfWork context, BinInventory inventory,
            int stockAreaId)
        {
            var rep = new Repository<Inventory>(context);
            inventory.StockAreaId = stockAreaId;
            inventory.Deleted = false;
            rep.Add(inventory);
        }

        /// <summary>
        /// Updates the inventory.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="inventory">The inventory.</param>
        public void UpdateInventory(IUnitOfWork context, BinInventory inventory)
        {
            var rep = new Repository<Inventory>(context);

            var entity = rep.GetInventoryById(inventory.Id);
            inventory.CopyTo(entity);
            entity.StockAreaId = inventory.StockAreaId;
            entity.CategoryId = inventory.CategoryId;
            entity.ItemId = inventory.ItemId;
            SetStockStatus(inventory.StockStatus, entity);
            if (entity.Container != null && inventory.Container != null)
            {
                (inventory.Container as LabeledShelfContainer).CopyTo(
                    entity.Container as LabeledShelfContainer);
            }
            else if (entity.Container == null && inventory.Container != null)
            {
                entity.Container = inventory.Container;
            }
            else
            {
                // Nothing to do.
            }
            if (inventory.Trigger != null)
            {
                if (entity.Trigger.GetType() == inventory.Trigger.GetType())
                {
                    if (entity.Trigger is DoorTrigger)
                    {
                        (inventory.Trigger as DoorTrigger).CopyTo(
                            entity.Trigger as DoorTrigger);
                    }
                    else if (entity.Trigger is DailyTrigger)
                    {
                        (inventory.Trigger as DailyTrigger).CopyTo(
                            entity.Trigger as DailyTrigger);
                    }
                    else if (entity.Trigger is WeeklyTrigger)
                    {
                        (inventory.Trigger as WeeklyTrigger).CopyTo(
                            entity.Trigger as WeeklyTrigger);
                    }
                    else
                    {
                        // TODO Add code here to support other triggers.
                    }
                }
                else
                {
                    entity.Trigger.Deleted = true;
                    entity.Trigger = inventory.Trigger;
                } 
            }
        }

        public void UpdateInventoriesStockStatus(IUnitOfWork context,
            ICollection<BinInventory> inventories, StockStatus stockStatus)
        {
            var rep = new Repository<Inventory>(context);

            foreach (var item in inventories)
            {
                rep.Attach(item);
                SetStockStatus(stockStatus, item);
            }
        }

        public void UpdateInventoryStockStatus(IUnitOfWork context, string SSID,
            StockStatus stockStatus)
        {
            var rep = new Repository<Inventory>(context);

            var inventory = rep.GetInventoryBySSID(SSID);
            SetStockStatus(stockStatus, inventory);
        }

        public void DeleteInventory(IUnitOfWork context, int inventoryId)
        {
            var rep = new Repository<Inventory>(context);

            var inventory = rep.GetInventoryById(inventoryId);
            inventory.Container.Deleted = true;
            inventory.Trigger.Deleted = true;
            inventory.Deleted = true;
        }

        public void DeleteInventoriesOfStockAreas(IUnitOfWork context,
            IEnumerable<int> stockAreaIdList)
        {
            var rep = new Repository<Inventory>(context);

            rep.DeleteInventories(o => IsInStockAreas(o, stockAreaIdList));
        }

        public void DeleteInventoriesOfItems(IUnitOfWork context,
            IEnumerable<int> itemIdList)
        {
            var rep = new Repository<Inventory>(context);

            rep.DeleteInventories(o => IsItems(o, itemIdList));
        }

        public IList<BinInventory> GetInventoriesOfStockAreas(IUnitOfWork context,
            IEnumerable<int> stockAreaIdList)
        {
            var rep = new Repository<Inventory>(context);

            return rep.GetInventories(o => IsInStockAreas(o, stockAreaIdList));
        }

        public IList<BinInventory> GetRestockList(IUnitOfWork context,
            IEnumerable<int> stockAreaIdList)
        {
            var rep = new Repository<Inventory>(context);

            return rep.GetInventories(o => o.Deleted == false
                && (o.StockStatus == StockStatus.StockOut
                    || o.StockStatus == StockStatus.Restock));
        }

        public BinInventory GetInventoryById(IUnitOfWork context, int id)
        {
            var rep = new Repository<Inventory>(context);

            return rep.GetInventoryById(id);
        }

        public BinInventory GetInventoryBySSID(IUnitOfWork context, string SSID)
        {
            var rep = new Repository<Inventory>(context);

            return rep.GetInventoryBySSID(SSID);
        }

        public IList<BinInventory> GetInventoriesByItem(IUnitOfWork context,
            int itemId)
        {
            var rep = new Repository<Inventory>(context);

            return rep.GetInventories(o => IsItems(o, new int[] { itemId }));
        }

        public BinInventory CloneInventory(IUnitOfWork context, int inventoryId)
        {
            var rep = new Repository<Inventory>(context);
            var entity = rep.GetInventoryById(inventoryId);
            var inventory = entity.Clone();
            rep.Add(inventory);
            return inventory;
        }

        public IList<BinInventory> CloneInventoriesOfStockArea(IUnitOfWork context,
            int fromStockAreaId, int toStockAreaId)
        {
            var rep = new Repository<Inventory>(context);

            var entities = rep.GetInventories(o => IsInStockAreas(o, new int[] { fromStockAreaId }));
            var inventories = new List<BinInventory>();
            foreach (var item in entities)
            {
                var inventory = item.Clone();
                inventory.StockAreaId = toStockAreaId;
                inventories.Add(inventory);
            }
            foreach (var item in inventories)
            {
                rep.Add(item);
            }
            return inventories;
        }

        #endregion

        #region Private Methods

        private static bool IsInStockAreas(Inventory entity, IEnumerable<int> idList)
        {
            return entity.Deleted == false && idList.Contains(entity.StockAreaId);
        }

        private static bool IsItems(Inventory entity, IEnumerable<int> idList)
        {
            return entity.Deleted == false && idList.Contains(entity.ItemId);
        }

        private static void SetStockStatus(StockStatus stockStatus, BinInventory inventory)
        {
            if (inventory.StockStatus != StockStatus.Restocking || stockStatus != StockStatus.StockOut)
            {
                inventory.StockStatus = stockStatus;
            }
        }

        #endregion
    }
}
